﻿using System;
using System.IO;
using System.Diagnostics;
using System.Configuration;
using System.Threading;

namespace oViT.FileWatcher
{
    public class Watcher
    {
        #region Variables
        FileSystemWatcher watcher = new FileSystemWatcher();
        string folderpath = string.Empty; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["FolderPath"] != null ? ConfigurationManager.AppSettings["FolderPath"].ToString() : String.Empty);
        string filter = string.Empty; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["Filter"] != null ? ConfigurationManager.AppSettings["Filter"].ToString() : String.Empty);
        bool subdirectories = false; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["IncludeSubDirectories"] != null ? Boolean.Parse(ConfigurationManager.AppSettings["IncludeSubDirectories"].ToString()) : false);
        string mak = string.Empty; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["MAK"] != null ? ConfigurationManager.AppSettings["MAK"].ToString() : String.Empty);
        string outputdirectory = string.Empty; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["OutputDirectory"] != null ? ConfigurationManager.AppSettings["OutputDirectory"].ToString() : String.Empty);
        string extension = string.Empty; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["DefaultFileExtension"] != null ? ConfigurationManager.AppSettings["DefaultFileExtension"].ToString() : ".mpeg");
        string processpath = string.Empty; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["ApplicationPath"] != null ? ConfigurationManager.AppSettings["ApplicationPath"].ToString() : String.Empty);
        bool removefile = false; //(ConfigurationManager.AppSettings != null && ConfigurationManager.AppSettings["RemoveProcessedFiles"] != null ? Boolean.Parse(ConfigurationManager.AppSettings["RemoveProcessedFiles"].ToString()) : false);
        bool processonstartup = false;

        //private const int RETRY_MAX_TIME = 1800000; // 30 hour
        //private const int RETRY_DELAY = 2000; // 10 seconds
        private bool started = false;

        public delegate FileProcessor MethodDelegate();
        public delegate void MultiFileProcessorDelegate();
        #endregion

        #region Properties
        /// <summary>
        /// full path to the directory to monitor
        /// </summary>
        public String FolderPath
        {
            get { return folderpath; }
            set { folderpath = value; }
        }

        /// <summary>
        /// filter used to determine what files are monitored in the directory
        /// </summary>
        public String Filter
        {
            get { return filter; }
            set { filter = value; }
        }

        /// <summary>
        /// whether or not to scan any sub directories in the monitored directory
        /// </summary>
        public Boolean InclSubDirectories
        {
            get { return subdirectories; }
            set { subdirectories = value; }
        }

        /// <summary>
        /// unique TiVo machine id
        /// </summary>
        public String MAK
        {
            get { return mak; }
            set { mak = value; }
        }

        /// <summary>
        /// full path to the directory where the processed files are stored
        /// </summary>
        public String OutputDirectory
        {
            get { return outputdirectory; }
            set { outputdirectory = value; }
        }

        /// <summary>
        /// file extension for the processed file, .mpeg
        /// </summary>
        public String Extension
        {
            get { return extension; }
            set { extension = value; }
        }

        /// <summary>
        /// full path to the TiVo decode tool, tivodecode.exe
        /// </summary>
        public String ProcessPath
        {
            get { return processpath; }
            set { processpath = value; }
        }

        /// <summary>
        /// whether or not to remove the processed file; post-processing
        /// </summary>
        public Boolean RemoveFile
        {
            get { return removefile; }
            set { removefile = value; }
        }

        /// <summary>
        /// whether or not to automatically start processing any .tivo files on application startup
        /// </summary>
        public Boolean ProcessOnStartup
        {
            get { return processonstartup; }
            set { processonstartup = value; }
        }

        /// <summary>
        /// whether or not the process has started
        /// </summary>
        public Boolean IsStarted
        {
            get { return started; }
            set { started = value; }
        }
        #endregion

        #region Constructors
        /// <summary>
        /// default constructor
        /// </summary>
        public Watcher()
        {
            watcher.Created += new FileSystemEventHandler(watcher_Created);
        }
        #endregion

        #region Methods
        /// <summary>
        /// kick off the watcher process to monitor any files in the FolderPath
        /// </summary>
        public void Start()
        {
            // if the monitor has not already started, start it
            if (!IsStarted)
            {
                // check if the path exists
                if (!String.IsNullOrEmpty(FolderPath))
                {
                    // define the folder to monitor
                    watcher.Path = FolderPath;
                    watcher.Filter = Filter;
                    watcher.IncludeSubdirectories = InclSubDirectories;

                    watcher.EnableRaisingEvents = true;

                    IsStarted = true;

                    if (ProcessOnStartup)
                    {
                        // start processing any files in a separate thread
                        MultiFileProcessorDelegate fpdelegate = new MultiFileProcessorDelegate(ProcessExistingFiles);
                        fpdelegate.BeginInvoke(null, fpdelegate);
                    }
                }
                else
                {
                    throw new ArgumentException("FolderPath not set!");
                }
            }
        }

        /// <summary>
        /// halt monitoring the FolderPath
        /// </summary>
        public void Stop()
        {
            watcher.EnableRaisingEvents = false;
            IsStarted = false;
        }

        /// <summary>
        /// begin processing the source files
        /// </summary>
        private void ProcessExistingFiles()
        {
            DirectoryInfo di = new DirectoryInfo(FolderPath);
            FileInfo[] files = di.GetFiles((String.IsNullOrEmpty(Filter) == false ? Filter : ""));

            // look at each file and process
            foreach (FileInfo f in files)
            {
                // stop the loop if the process was stopped
                if (IsStarted)
                {
                    FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, f.DirectoryName, f.Name);

                    try
                    {
                        if (FileCreated != null)
                            FileCreated(this, e);

                        FileProcessor fp = new FileProcessor(ProcessPath, MAK, OutputDirectory, f.FullName, f.Name, this.Extension, RemoveFile);

                        // Create the callback delegate.
                        //AsyncCallback cb = new AsyncCallback(MyAsyncCallback);

                        //MethodDelegate fpdelegate = new MethodDelegate(fp.BeginProcessing);
                        //fpdelegate.BeginInvoke(cb, fpdelegate);

                        //string output = "?????";
                        //string output = fp.BeginProcessing();
                        fp.BeginProcessing();

                        if (FileProcessed != null)
                            FileProcessed(this, new WatcherEventArgs(e, String.Format("Done...{0}\r\nProcess finished.\r\n", fp.Output)));
                    }
                    catch (Exception ex)
                    {
                        if (FileProcessed != null)
                            FileProcessed(e, new WatcherEventArgs(e, String.Format("{0}\r\n", ex.Message)));
                    }
                }
            }
        }

        /// <summary>
        /// asynchronous callback
        /// </summary>
        /// <param name="ar">the status of the asynchronous operation</param>
        public void MyAsyncCallback(IAsyncResult ar)
        {
            // Because you passed your original delegate in the asyncState parameter
            // of the Begin call, you can get it back here to complete the call.
            MethodDelegate dlgt = (MethodDelegate)ar.AsyncState;

            // Complete the call.
            FileProcessor fp = dlgt.EndInvoke(ar);

            // wait a few seconds
            Thread.Sleep(2000);

            FileSystemEventArgs e = new FileSystemEventArgs(WatcherChangeTypes.Created, fp.FullSourcePath, fp.SourceFileName);

            // TODO: this isn't writing to the Textbox on the MainWindow caller
            if (FileProcessed != null)
                FileProcessed(fp, new WatcherEventArgs(e, String.Format("Done...{0}\r\nProcess finished.\r\n", fp.Output)));
        }

        #endregion

        #region Events
        /// <summary>
        /// watcher event - used when the watcher is started
        /// </summary>
        /// <param name="sender">object sender</param>
        /// <param name="e">directory event</param>
        void watcher_Created(object sender, FileSystemEventArgs e)
        {
            try
            {
                // run the event if it exists
                if (FileCreated != null)
                    FileCreated(sender, e);

                FileProcessor fp = new FileProcessor(ProcessPath, MAK, OutputDirectory, e.FullPath, e.Name, this.Extension, RemoveFile);

                // Create the callback delegate.
                AsyncCallback cb = new AsyncCallback(MyAsyncCallback);

                // invoke the delegate
                MethodDelegate fpdelegate = new MethodDelegate(fp.BeginProcessing);
                fpdelegate.BeginInvoke(cb, fpdelegate);

                // crreate a thread and run the process in that
                //Thread backgroundThread;
                //backgroundThread = new Thread(new ThreadStart(fp.BeginProcessing));
                //backgroundThread.Name = "FileProcessing";
                //backgroundThread.Start();

                //fp.BeginProcessing();

                //if (FileProcessed != null)
                //    FileProcessed(sender, new WatcherEventArgs(e, String.Format("{0}\r\n", fp.Output)));
                //FileProcessed(sender, new WatcherEventArgs(e, "Process finished.\r\n"));
            }
            catch (Exception ex)
            {
                if (FileProcessed != null)
                    FileProcessed(sender, new WatcherEventArgs(e, String.Format("{0}\r\n", ex.Message)));
            }
        }

        /*void watcher_Created(object sender, FileSystemEventArgs e)
        {
            try
            {
                if (FileCreated != null)
                    FileCreated(this, e);

                FileProcessor fp = new FileProcessor(ProcessPath, MAK, OutputDirectory, e.FullPath, e.Name, this.Extension, RemoveFile);

                fp.BeginProcessing();

                if (FileProcessed != null)
                    FileProcessed(this, new WatcherEventArgs(e, String.Format("Done...{0}\r\nProcess finished.\r\n", fp.Output)));
            }
            catch (Exception ex)
            {
                if (FileProcessed != null)
                    FileProcessed(e, new WatcherEventArgs(e, String.Format("{0}\r\n", ex.Message)));
            }
        }*/
        #endregion

        #region Custom Events
        public delegate void WatcherEventHandler(object sender, FileSystemEventArgs args);
        public delegate void WatcherProcessHandler(object sender, WatcherEventArgs args);

        public event WatcherEventHandler FileCreated;
        public event WatcherProcessHandler FileProcessed;
        #endregion
    }
}
